package com.fundamental.lock;

import com.sun.corba.se.impl.orbutil.concurrent.Sync;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.locks.AbstractOwnableSynchronizer;

/**
 * 最简单的 独占，等待，非抢占式锁。   获取时判断当前锁资源的占有者是不是自己，不是的话一直自旋cas去获取。
 */
public class SimpleExclusiveSync implements Sync {

    private static final Unsafe unsafe;

    private static final long holdThreadOffset;

    static {
        try {

            Class<Unsafe> clazz = Unsafe.class;
            Field field = clazz.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);

            holdThreadOffset = unsafe.objectFieldOffset(SimpleExclusiveSync.class.getDeclaredField("exclusiveOwnerThread"));
        }catch (Exception e){
            throw new Error(e);
        }
    }

    Thread exclusiveOwnerThread;

    @Override
    public void acquire() throws InterruptedException {

        Thread current;
        if(unsafe.getObject(this,holdThreadOffset) != (current = Thread.currentThread())) {
            while (!unsafe.compareAndSwapObject(this, holdThreadOffset, null, Thread.currentThread())){

            }
        }
    }

    @Override
    public boolean attempt(long msecs) throws InterruptedException {

        long endTime = System.currentTimeMillis() + msecs;
        Thread current;
        if(unsafe.getObject(this,holdThreadOffset) != (current = Thread.currentThread())) {
            while (!unsafe.compareAndSwapObject(this, holdThreadOffset, null, Thread.currentThread())){
                if(System.currentTimeMillis() >= endTime){
                    throw new RuntimeException("attempt fail");
                }
            }
        }

        return false;
    }

    @Override
    public void release() {
        Thread current;
        if(unsafe.getObject(this,holdThreadOffset) != (current = Thread.currentThread())){
            throw new RuntimeException("current thread do not own sync");
        }
        unsafe.getAndSetObject(this,holdThreadOffset,null);
    }
}
